<template>
  <WebGLCanvas :id="id"/>
</template>

<script setup lang="ts">
  import { Engine, Scene, ArcRotateCamera, UniversalCamera, Vector3,
    HemisphericLight, PointLight, DirectionalLight, SpotLight,
    MeshBuilder, Color3, 
    StandardMaterial, MultiMaterial, Texture, Color4, Vector4,
    Animation
  } from '@babylonjs/core'

  import Girls from '~/assets/images/babylon/sprite/girls.jpeg'

  const id = 'animations'
  useHead({
    title: '动画-让3D元素动起来'
  })

  const initBabylon = () => {
    // 初始化canvas
    const canvas = document.querySelector<HTMLCanvasElement>(`#${id}`)
    if (!canvas) {
      throw new Error('Canvas not found.')
    }
    canvas.width = window.innerWidth
    canvas.height = window.innerHeight
    // 初始化babylon 3D Engine, 开启抗锯齿
    /** 
     * “抗锯齿(英语:anti-aliasing,简称AA),
     * 也译为边缘柔化、消除混叠、抗图像折叠有损等。
     * 它是一种消除显示器输出的画面中图物边缘出现凹凸锯齿的技术,
     * 那些凹凸的锯齿通常因为高分辨率的信号以低分辨率表示或无法准确运算出3D图形坐标定位时所导致的图形混叠(aliasing)而产生的,
     * 反锯齿技术能有效地解决这些问题。
     */
    const engine = new Engine(canvas, true)

    // 创建一个场景
    const scene = new Scene(engine)
    // 要看到漫反射和镜面反射的材质效果，必须要求创建至少一个光源。
    // 要让环境背光照射出材质的环境颜色效果，还需要为场景设置一个环境颜色
    // scene.ambientColor = new Color3(0.1, 0.4, 1)
    scene.clearColor = new Color4(0, 0, 0, 1)

    // 添加一个通用相机
    // const camera = new UniversalCamera(
    //   'camera',
    //   new Vector3(0, 0, -10),
    //   scene
    // )

    // 添加一个弧形旋转相机
    const camera = new ArcRotateCamera(
      'camera',
      0,
      0,
      20,
      new Vector3(0, 0, 0),
      scene
    )
    camera.setPosition(new Vector3(0, 0, 10))

    // 绑定鼠标事件
    camera.attachControl(canvas, true)
    // 添加一组灯光到场景
    // 半球光是模拟环境光的简便方法
    // const light1 = new HemisphericLight('light1', new Vector3(1, 1, 1), scene)
    // const light2 = new PointLight('light2', new Vector3(5, 5, 5), scene)
    const light3 = new DirectionalLight('light3', new Vector3(-10, 10, 10), scene)
    light3.intensity = 0.5
    // 聚光灯由位置position，方向direction，角度angle和指数exponent定义。
    // 上面的数值决定了圆锥形光束该从什么位置开始，然后朝哪个方向照射。
    // const light4 = new SpotLight(
    //   'light4',
    //   new Vector3(0, 30, -10),
    //   new Vector3(0, -1, 0),
    //   Math.PI / 3,
    //   2,
    //   scene
    // )

    
    // 创建一个材质
    // const materials = new StandardMaterial('material', scene)
    // 漫反射颜色
    // materials.diffuseColor = new Color3(1, 0, 1)
    // 镜面颜色
    // materials.specularColor = new Color3(0.5, 0.6, 0.87)
    // 自发光颜色
    // materials.emissiveColor = new Color3(0.18, 0.35, 0.68)
    // 环境光颜色
    // materials.ambientColor = new Color3(0.23, 0.98, 0.53)

    /**
     * 创建一个正方体
     * 雪碧图 800*3 * 800*2 画图如下 
     * 1    *       *       *
     * 1/2  *       *       *
     * 0    1/3     2/3     1
     * @see [texturePerBoxFace](https://doc.babylonjs.com/features/featuresDeepDive/materials/using/texturePerBoxFace)
     * babylonjs官方公式：c = 6 r = 4
     * faceUV[f] = ((c * 1) / 6, (r * 1) / 4, ((c + 1) * 1) / 6, ((r + 1) * 1) / 4);
     */
    const box = MeshBuilder.CreateBox(
      'box',
      {
        size: 3,
        faceUV: [
          new Vector4(0/3, 1/2, 1/3, 1), // 从左到右从上到下 第1张
          new Vector4(1/3, 1/2, 2/3, 1), // 从左到右从上到下 第2张
          new Vector4(2/3, 1/2, 3/3, 1), // 从左到右从上到下 第3张
          new Vector4(0/3, 0/2, 1/3, 1/2), // 从左到右从上到下 第4张
          new Vector4(1/3, 0/2, 2/3, 1/2), // 从左到右从上到下 第5张
          new Vector4(2/3, 0/2, 3/3, 1/2), // 从左到右从上到下 第6张
        ]
      },
      scene
    )
    box.position = new Vector3(0, 0, 0)
    box.rotation = new Vector3(10, 10, 0)

    const materail = new StandardMaterial('materail', scene)
    materail.diffuseColor = Color3.Purple()

    // materail.diffuseTexture = new Texture(Girls, scene)
    // materail.specularTexture = new Texture(Girls, scene)
    materail.emissiveTexture = new Texture(Girls, scene)
    // materail.ambientTexture = new Texture(Girls, scene)
    box.material = materail

    // 创建动画
    // const animation = new Animation(
    //   'animation',
    //   'rotation.x', // 在X轴的方向缩放box
    //   30, // 每秒请求的帧数：动画中可能达到的最高FPS
    //   Animation.ANIMATIONTYPE_FLOAT,  // 数值变化类型
    //                                   // 浮点数：BABYLON.Animation.ANIMATIONTYPE_FLOAT
    //                                   // 二维向量：BABYLON.Animation.ANIMATIONTYPE_VECTOR2
    //                                   // 三维向量：BABYLON.Animation.ANIMATIONTYPE_VECTOR3
    //                                   // 四元数：BABYLON.Animation.ANIMATIONTYPE_QUATERNION
    //                                   // 矩阵：BABYLON.Animation.ANIMATIONTYPE_MATRIX
    //                                   // 颜色：BABYLON.Animation.ANIMATIONTYPE_COLOR3
    //   Animation.ANIMATIONLOOPMODE_CYCLE // 动画的在执行完一个周期后，需要执行的行为模式
    //                                     // ANIMATIONLOOPMODE_RELATIVE 相对，相对运动，即：执行完一次，在接着最后的状态，继续执行
    //                                     // ANIMATIONLOOPMODE_CYCLE 循环，动画循环执行，即：执行完一次，从头开始再执行
    //                                     // ANIMATIONLOOPMODE_CONSTANT 常量，动画执行一次就停止不动了
    // )
    // // 立方体缩放的动画，但是不只是简单的均速缩放，需要做一些修饰：
    // // 放大的过程应该快一些，而缩小的过程应该慢一些
    // animation.setKeys([
    //   { frame: 0, value: 0 },
    //   { frame: 100, value: 2*Math.PI }
    // ])
    // // 把动画和物体关联起来
    // box.animations = []
    // box.animations.push(animation)
    // // 启动动画
    // const runAnimationBox = scene.beginAnimation(box, 0, 100, true, 0.2)

    // 循环创建动画
    const animationPropertyNames = ['rotation.x', 'rotation.y', 'rotation.z']
    const allAnimations = []
    animationPropertyNames.forEach((name, index) => {
      const animation = new Animation(
        `boxAnimation${index}`,
        name,
        30,
        Animation.ANIMATIONTYPE_FLOAT,
        Animation.ANIMATIONLOOPMODE_CYCLE
      )
      animation.setKeys([
        { frame: 0, value: 0 },
        { frame: 100, value: 4*Math.PI }
      ])
      box.animations.push(animation)
      const runAnimationBox = scene.beginAnimation(box, 0, 100, true, 0.1*(index+1))
      allAnimations.push(runAnimationBox)
    })


    // 最后一步调用engine的runRenderLoop方案，
    // 执行scene.render()，让我们的3d场景渲染起来
    engine.runRenderLoop(() => {
      scene.render()
    })

    // 监听浏览器改变大小的事件，通过调用engine.resize()来自适应窗口大小
    window.addEventListener('resize', () => {
      engine.resize()
    })
  }

  onMounted(() => {
    initBabylon()
  })
</script>